from types import ListType
import time
import sqlite3
import os

import builderPie.demjson as json

conn=sqlite3.connect('../data/build_info.db')
conn.row_factory=sqlite3.Row

timeFmt='%m/%d %H:%M:%S'

def error(code, msg):
    details=(
        "null", 
        {
            "code": code,
            "message": msg
        }
    )
    
    return details

def tableExists(tablename):
    tables=[]
    for row in conn.execute("select * from sqlite_master"):
        tables.append(row[1])
        
    if tablename in tables: return True
    return False
    
def initActiveBuilds():
    columns='id integer primary key, name text, method text, location text, buildInfo text, testInfo text';
    conn.execute('create table activeBuilds (%s)' % columns)
    conn.commit()
    
def initBuildLog():
    columns='id integer primary key, buildId integer, key text, '
    columns+='returnCode integer, buildInfo text, testInfo text, timestamp integer'
    conn.execute('create table buildLog (%s)' % columns)
    conn.commit()
    
def serveDetails(path):
    if not tableExists('activeBuilds'): return 'There are no active builds available.'
    
    lines=[]

    endPos=path.rfind('/')
    key=path[9:endPos]

    lines.append('<h2>Details for %s</h2>' % key)

    for row in conn.execute('select * from buildLog where key = ? order by timestamp desc', (key,)):
        buildId=row['buildId']
        buildInfo=json.decode(row['buildInfo'])
        
        success=True
        output=''
        error=''
        
        for buildCmd in buildInfo:
            if buildCmd['returnCode']==1: success=False
            
            output+=buildCmd['stdout']
            error+=buildCmd['stderr']
            
        if success:
            buildStatus='was successful'
            buildColor='green'
        else:
            buildStatus='failed'
            buildColor='red'
        
        lines.append("<p><b><span style=\"color: %s;\">The build %s.</span></b></p>" % (buildColor, buildStatus))
        lines.append("<p><b>Output:</b>\n<pre>%s</pre></p>" % output)
        if error: lines.append("<p><b>Error:</b>\n<pre>%s</pre></p>" % error)

        testInfo=json.decode(row['testInfo'])
        
        if isinstance(testInfo, ListType):
            success=True
            output=''
            error=''
            
            for test in testInfo:
                if test['returnCode']==1: success=False
                
                output+=test['stdout']
                error+=test['stderr']
            
            if success:
                testStatus='were successful'
                testColor='green'
            else:
                testStatus='failed'
                testColor='red'
                
            lines.append("<p><b><span style=\"color: %s;\">The test(s) %s.</span></b></p>" % (testColor, testStatus))
            lines.append("<p><b>Testing Output:</b>\n<pre>%s</pre></p>" % output)
            if error: lines.append("<p><b>Testing Error:</b>\n<pre>%s</pre></p>" % error)
        else:
            lines.append("<p><b>%s</b><p/>" % testInfo);
        
        lines.append("<hr>")
        
    lines.pop()
    
    args=('select name from activeBuilds where id = ?', (buildId,))

    row=conn.execute(*args).fetchone()
    buildName=row['name']
    
    lines.append('<a href="/builds/%s/">' % buildId)
    lines.append('&laquo; Back to build information for %s</a>' % buildName)
    
    return "".join(lines)

def serveRoot():
    if not tableExists('activeBuilds'):
        return ('There are no active builds available.', [], 200)
    
    headers=[]
    response=200
    
    lines=[]

    builds=[]
    args=('select id, name from activeBuilds')

    for row in conn.execute(args):
        builds.append(row)
        
    if len(builds)>1:
        lines.append("<b>Select a build:</b>\n<ul>")
        for build in builds:
            name=build['name']
            id=build['id']
            if not name: name='BuildId %s' % id
            
            lines.append("<li><a href=\"/builds/%s/\">%s</a></li>" % (id, name))
        lines.append("</ul>")
    else:
        headers.append(('Location','/builds/%s/' % builds[0]['id']))
        response=302
        
    return ("".join(lines), headers, response)

    
def serveBuildStats(path):
    if not tableExists('activeBuilds'): return 'There are no active builds available.'
    
    if not tableExists('buildLog'): 
        lines.append('<p>There is no build information available.</p>')
        lines.append('<a href="/">&laquo; Back to build list</a>')
        
        return "".join(lines)
        
    lines=[]
    
    endPos=path.rfind('/')
    buildId=path[8:endPos]
    
    row=conn.execute('select name from activeBuilds where id=?',(buildId,)).fetchone()
    
    name=row['name']
    if not name: name='BuildId %s' % buildId
    
    lines.append('<h2>%s</h2>' % name)
    
    builds=[]
    
    for row in conn.execute('select * from buildLog where buildId = ? order by timestamp desc', (buildId,)):
        if row['key'] not in builds:
            testStatus='none'
            if row['returnCode']==0: buildStatus='success'
            else: buildStatus='failure'
            
            timeObj=time.localtime(row['timestamp'])

            lines.append("<div class=\"%s build\">&nbsp;</div> " % buildStatus)
            lines.append("<div class=\"%s test\">&nbsp;</div> " % testStatus)
            lines.append("<a href=\"/details/%s/\">[details]</a> " % row['key'])
            lines.append("%s | " % time.strftime(timeFmt,timeObj))
            lines.append("%s<br><br>" % row['key'])

            builds.append(row['key'])
            
    if len(builds)==0: lines.append('<p>There is no build information available.</p>')

    lines.append('<a href="/">&laquo; Back to build list</a>')
    
    return "".join(lines)

def postResults(rpc):
    #<--- builds with multiple commands only save the results of the first command

    returnCode=0
    for build in rpc['params']['build']:
        if build['returnCode']==1: returnCode=1
        
    columns='buildId, key, returnCode, buildInfo, testInfo, timestamp'
    
    args=(
        'insert into buildLog (%s) values(?, ?, ?, ?, ?, ?)' % columns,
        (
            rpc['params']['buildId'],
            rpc['id'],
            returnCode,
            json.encode(rpc['params']['build']),
            json.encode(rpc['params']['tests']),
            time.time()
        )
    )
    
    if not tableExists('buildLog'): initBuildLog()
    
    conn.execute(*args)
    conn.commit()

    return ("results processed", "null")
    
    
def getBuildList(rpc):
    args=('select id, name from activeBuilds')
    
    reply=[]
    
    if not tableExists('activeBuilds'): return error(-32602, "No active builds available.")
    
    for row in conn.execute(args):
        reply.append({'name': row['name'],'id': row['id']})

    return (reply, "null")
    


def getBuildInfo(rpc):
    if not tableExists('activeBuilds'): return error(-32602, "No active builds available.")

    reply={}
    
    if 'buildName' in rpc['params']: 
        args=('select * from activeBuilds where name = ?', (rpc['params']['buildName'],))
    
    if 'buildId' in rpc['params']: 
        args=('select * from activeBuilds where id = ?', (rpc['params']['buildId'],))

    if 'buildName' not in rpc['params'] and 'buildId' not in rpc['params']:
        return error(-32604, "neither buildName or buildId was provided.")
        
    row=conn.execute(*args).fetchone()
    if len(row)==1: return error(-32603, "No active builds named '%s'." % name)
    
    reply={
        'id': row['id'],
        'location': row['location'],
        'method': row['method'],
        'buildInfo': json.decode(row['buildInfo']),
        'testInfo': json.decode(row['testInfo'])
    }

    return (reply, "null")
